真 · 2.5D 视角!Cocos Creator 实现《饥荒》同款视觉表现
有一些游戏,明明是 2D 素材却能整出 3D 效果,比如《饥荒》、《八方旅人》等。「Cocos Star Writer」Nowpaper 做了个有趣的尝试,使用 Creator 3.3 实现了这一效果,并整理了文字+视频+Demo 工程,供大家参考。
Demo 实现效果
大家好,我是 Nowpaper。《八方旅人》和《饥荒》的游戏视觉呈现非常有趣,你能清楚的知道它是 2D 素材,却呈现出一种 3D 世界的感觉,这种 2D 和 3D 混合的游戏,在游戏历史中很早就有出现,比如《轩辕伏魔录》、《空之轨迹》、《仙境传说》等等。
其实它的实现原理并不复杂,就是在游戏世界中,实现 2D 素材 3D 化的一种制作手段;当然了反过来也可以,因此只要游戏引擎支持 2D 和 3D 的渲染,就能实现同样的立体效果。
目前,在 Cocos Creator 3.x 中并不能非常容易地实现这个效果,我也是经过一番折腾才找到了方法,希望能够抛砖引玉,发掘更有趣的玩法。
资源链接
Cocos Store丨Demo 工程:
https://store.cocos.com/app/detail/3373
视频教程丨文末【阅读原文】跳转:
https://www.bilibili.com/video/BV1jg411K7C2
论坛讨论帖:
https://forum.cocos.org/t/topic/124531
注:下文中提到的脚本/代码请移步 Cocos Store 下载 Demo 工程查看。
实现思路
按照正常的思考套路,应该将素材导入到工程,然后使用 2D 精灵,给 3D 化。但是这个方式并不可行。来通过这个例子来证明,放一个图片进入 Creator 工程,然后新建 Canvas,将图片拖进画布,我们在 3D 空间中,可以看到这个精灵。按说这个精灵,应该可以在 3D 空间中渲染,但是很遗憾,它并不能出现在 3D 空间中,只能在 Canvas 里被渲染。来尝试一下就知道了,直接在画布上,进行 3D 的 X 轴旋转,你会发现图像的模型已经变换了,但是图像的画面还是 2D 的。
因此这个方案并不可行,那么只能用其他的方式来解决。最繁琐的一个方式,就是将 2D 素材全部以纹理的形式,制作成各种材质,然后贴到一个方片网格上,就如我现在实现的这样。
虽然可以在 3D 空间里展示了,但是你会发现,这个太麻烦,需要对所有的素材进行材质创建,而且适配和制作动画也比较麻烦。所以,我们将采用 Cocos Creator 支持的 2D 渲染的资产类型,那就是 Spine、龙骨和 Tiledmap 解决。因为在我的各种实验中,只有这3种类型的资产,在画布上可以进行 3D 空间各个轴的变换,图像也会跟着变化,而不是像 Sprite 图像一样,不会有 3D 空间变化。
既然如此,那么我们就用龙骨来实现 2D 的人物动画和物件的素材渲染。我用龙骨软件,经过一番操作,实现了一个小人的行走动画、一个树木的资产,关于地面,我准备了一个地块拼接图,直接放置到项目资产中了。
创建项目
(PS.高手请直接跳过,到下一 Part 视觉实现)
现在在新建项目当中,新建一个场景,建立一个 Canvas,拖一个地面的精灵,利用精灵的平铺特性,生成一个你觉得足够大的地图。当然了这里也可以使用 Tiledmap 来绘制地面,这里我就直接一个节点就搞定了。
在右上方打开约束控制设定,把约束设定成为你的地块格子宽高,我这个地块是80。这个功能是 Creator 3.3 版本新加入的,当时我盘点的时候就说这个改进非常有用,这不马上就用上了。因为有了坐标控制约束,在地图上摆放各种物件就显得非常舒服。
等设置完成后,我们在地图上加入一个节点层,将它命名为环境吧,然后在这个节点上种树。种树的操作就看个人的喜好了,放置的树是咱们用龙骨实现的那个(其实 Spine 也行)。种完树之后,把小人放到环境节点里。
首先实现人物的行走,这个脚本相对来说比较简单,只是需要自己实现一个按键的监听,处理 x、y 输入,先实现 wasd 按键的处理,在 Update 里面实现人物的移动的逻辑,您可以使用您熟悉脚本实现,我的项目中的行走脚本是 PlayerController.ts。
视觉实现
现在重头戏来了,让我们改变一下摄像机,让世界变成一个 3D 的环境。点击一下右上角的视角切换,变成 3D 模式,你会看到,其实 Canvas 就是一个面片网格,它本身就在 3D 空间当中。现在新建一个摄像机,用 Main 摄像机修改过去也可以,先把 Canvas 的摄像机给关闭掉,以后有 UI 了再说,将摄像机 X 轴旋转45度,稍微调整一下对准主角,我们可以看到摄像机里呈现的是 3D 样式。
这个时候可以试试修改一下树和主角,你会看到通过修改 X 轴的数值,能够让它们在 2D 画面中“立”起来,而这就是实现饥荒视角效果的秘诀。在这个阶段,其实就可以直接在 2D 画面中摆放 3D 环境了,但是物件实在太多,我就简单写了个脚本 EnvManager.ts。
在这个脚本中,让所有的环境子物体,全部根据摄像机的朝向旋转,顺便完成一下环境物体的排序,根据画家算法,让 Y 轴排序节点,实现近处遮挡远处的效果。将这个脚本添加到环境物体节点组上,并添加摄像机的引用,然后运行,你会看到所有的环境组中的子物体,全部都“立”了起来。
当然了这么看,还没有什么明显的效果,因为摄像机没动。
下面实现一下摄像机跟随和视角的变换。跟随是根据主角的位置进行位移,由于摄像机的已经在 3D 世界中,设置的坐标不好确定,这里我们采用一种控制点的做法,就是在主角的同样位置上,有一个控制节点一直同步主角的坐标,而摄像机则挂在这个节点上,这个节点移动,摄像机也跟着移动了。而实现世界旋转,只需要对它进行旋转即可,因为摄像机一直处于它的子坐标系中。脚本是:CameraController.ts。
在这个组件脚本里面,需要指定一个目标,在开始的时候计算出偏移量,然后在 Update 里面,不停的设置它的坐标就可以了。
视角旋转
在饥荒游戏中,视角可以呈现一定角度的旋转,我们就实现一个按 Q 和 E 来,实现左右旋转摄像机的脚本。
CameraController.ts 脚本监听 KeyDown 事件,实现一个旋转方法,当按下 QE 的时候调用这个方法。为了让旋转执行中不会被再次调用,我们用一个变量来锁定判断,然后用 Tween 来处理这个旋转。先取出来自身的角度,然后做一下 to 的缓动行为,成功之后去掉锁定变量,OK!在按键事件中进行调用,分别是-45和+45,具体代码,在 CameraController.ts 中。
由于摄像机的的旋转跟随了控制点,所以在环境物体管理脚本中,也得需要作一下修改,让角度变更跟着控制点刷新。脚本是 EnvManager.ts。
保存回到 Creator 中,将 CameraController 添加到摄像机的控制点上,运行看看效果吧。
两大缺陷的解决
但是就目前为止,还存在两个问题:
第一:旋转之后移动控制,还是按照之前2D的方向,并没有根据摄像机的旋转作调整,这就很别扭了;
第二:画家算法已经对旋转之后的画面,有了错误支持,你会发现人物的遮挡关系是错误的。
关于摄像机相对移动
我们需要进行一个数学的运算,在主角的脚本的 Update 里,计算出来的 inputX,乘以自身对应的 right 方向向量,inputY 乘以自身的 Up 方向向量,得出来的标量就是旋转过和屏幕对应的 x 和 y 的输入量了。因为主角跟随着摄像机在旋转,所以对应的方向了也发生了改变,如果你的主角不是这样的,请注意计算方法,否则可能会出错。
核心代码:
const right = this.node.right.clone().multiplyScalar(inputx);
const up = this.node.up.clone().multiplyScalar(inputy);
const newInputXY = right.add(up).normalize();
关于 2.5D 空间的绘制排序
第二问题是视角旋转之后,物体渲染排序不对。这个咱们使用距离计算的方式解决,也就是说距离摄像机越远的越先绘制,这样近处的排在后面,遮挡关系就对。在环境物体管理器脚本中,我们需要计算一下物体和摄像机的世界坐标距离,然后在排序中进行计算,得出新的顺序。
核心代码:
let d1:Vec3 = v3(),d2:Vec3 = v3();
const worldPosition = this.camera.node.worldPosition;
this.node.children.sort((a,b)=>{
Vec3.subtract(this.d1,a.worldPosition,worldPosition);
Vec3.subtract(this.d2,b.worldPosition,worldPosition);
return this.d2.length() - this.d1.length();
})
结语
至此,《饥荒》同款视角 DEMO 已经完成了,如果想更精致的效果,还得需要更多的美术资源支持,甚至 shader、后期处理之类的配合。就目前而言,Creator 的有趣玩法,还没有完全挖掘出来,期待更多的开发者找出好玩的技术点子!
如果本次分享对您有帮助,欢迎点击文末【阅读原文】关注我的 B 站,后续将会有更多 Cocos Creator 游戏开发分享。我是 Nowpaper,一个混迹游戏行业的老爸,我们下次再见!